{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# D3中的选集\n",
    "\n",
    "\n",
    "选集（selection）是基于D3的可视化项目的重要基础之一，用来定位页面上的特定视觉元素。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Document { location: [Getter/Setter] }"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "var jsdom = require('jsdom');\n",
    "const {\n",
    "    JSDOM\n",
    "} = jsdom;\n",
    "const {\n",
    "    document\n",
    "} = (new JSDOM('')).window;\n",
    "global.document = document;\n",
    "var d3 = require(\"d3\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在可视化项目中，我们需要不断地对页面上不同元素进行选择、设定属性和方法等等操作，这将很快演变成为单调的重复性工作。为了减少开发中的琐碎工作，D3引入了自己的选择器API。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 选取单个元素\n",
    "\n",
    "在D3中，使用`d3.select()`方法来选取单个元素，该方法使用CSS选择器或操作对象为参数，并返回D3选集。这个选择器虽然可以选择多个元素，但是只返回选集中的第一个匹配的元素。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<p>hi</p>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "var svg = d3.select(document.body).append('p').text('hi')\n",
    "$$.html(document.body.innerHTML);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "所有的D3选集都支持一系列转化方式（the selection’s transformation methods），本例中用到的text()方法就是其中之一。下面列出部分常见转换方法。这些方法大都可以获取或设置元素的特性。\n",
    "\n",
    "* attr()\n",
    "* classed()\n",
    "* style()\n",
    "* text()\n",
    "* html()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Selection {\n",
       "  _groups: [ [ HTMLParagraphElement {} ] ],\n",
       "  _parents: [ HTMLHtmlElement {} ]\n",
       "}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 设定属性及其值\n",
    "d3.select(\"p\").attr(\"foo\", \"goo\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'goo'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 获取指定属性的值\n",
    "d3.select(\"p\").attr(\"foo\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "false"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 检查元素是否具有指定的类\n",
    "d3.select(\"p\").classed(\"goo\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<p foo=\"goo\" class=\"goo\">hi</p>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 为选定的元素增加类\n",
    "d3.select(\"p\").classed(\"goo\", true);\n",
    "$$.html(document.body.innerHTML);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<p foo=\"goo\" class=\"goo\">hi</p>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 还可以通过函数的方式，灵活增加类\n",
    "d3.select(\"p\").classed(\"goo\", function() {\n",
    "    return true;\n",
    "});\n",
    "$$.html(document.body.innerHTML);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10px \n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<p foo=\"goo\" class=\"goo\" style=\"font-size: 10px;\">hi</p>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 获取元素的某个样式的值\n",
    "d3.select(\"p\").style(\"font-size\", \"10px\");\n",
    "color = d3.select(\"p\").style(\"font-size\");\n",
    "console.log(color, d3.select(\"p\").style(\"color\"))\n",
    "$$.html(document.body.innerHTML);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'hi'"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 获取元素的文本内容\n",
    "d3.select(\"p\").text();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<p foo=\"goo\" class=\"goo\" style=\"font-size: 10px;\">Hello</p>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 设定元素的文本内容\n",
    "d3.select(\"p\").text(\"Hello\");\n",
    "$$.html(document.body.innerHTML);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<p foo=\"goo\" class=\"goo\" style=\"font-size: 10px;\">Sun Apr 19 2020 13:52:01 GMT+0800 (China Standard Time)</p>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 以函数的方式，设定内容\n",
    "d3.select(\"p\").text(function() {\n",
    "    return Date();\n",
    "});\n",
    "$$.html(document.body.innerHTML);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Sun Apr 19 2020 13:52:01 GMT+0800 (China Standard Time)'"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 获取元素内容\n",
    "d3.select(\"p\").html()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<p foo=\"goo\" class=\"goo\" style=\"font-size: 10px;\">Sun Apr 19 2020 13:52:01 GMT+0800 (China Standard Time) <span style=\"color: blue;\">D3.js</span></p>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 通过函数方式设定元素内容\n",
    "d3.select(\"p\").html(function() {\n",
    "    return d3.select(this).text() +\n",
    "        \" <span style='color: blue;'>D3.js</span>\";\n",
    "});\n",
    "$$.html(document.body.innerHTML);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 选取多个元素\n",
    "\n",
    "通常情况下，我们很少只选取单个元素。相反，大多数情况下是同时对页面上的多个元素进行特定处理。在下面的例子中，我们将介绍D3的多元素选择器及其API。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<p foo=\"goo\" class=\"goo\" style=\"font-size: 10px;\">Sun Apr 19 2020 13:52:01 GMT+0800 (China Standard Time) <span style=\"color: blue;\">D3.js</span></p><div></div><div></div><div></div>"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 增加若干个div元素\n",
    "for (i = 0; i < 3; i++) {\n",
    "    d3.select(document.body).append('div')\n",
    "}\n",
    "$$.html(document.body.innerHTML);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<p foo=\"goo\" class=\"goo\" style=\"font-size: 10px;\">Sun Apr 19 2020 13:52:01 GMT+0800 (China Standard Time) <span style=\"color: blue;\">D3.js</span></p><div style=\"background-color: rgb(204, 204, 204); width: 100px; height: 100px; margin: 10px;\"></div><div style=\"background-color: rgb(204, 204, 204); width: 100px; height: 100px; margin: 10px;\"></div><div style=\"background-color: rgb(204, 204, 204); width: 100px; height: 100px; margin: 10px;\"></div>"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 选中所有的div元素，并进行修改\n",
    "d3.selectAll(\"div\")\n",
    "    .style(\"background-color\", \"#ccc\")\n",
    "    .style(\"width\", \"100px\")\n",
    "    .style(\"height\", \"100px\")\n",
    "    .style(\"margin\", \"10px\");\n",
    "$$.html(document.body.innerHTML);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "转化方法，是基于选择集的，所以既适用于单个元素，也适用于多个元素组成的集合。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "还可对选集调用`each()`方法，分别处理每一个元素。`each()`方法依次传递当前数据（d）、当前索引（i）和当前组（节点），并将其作为当前DOM元素。此方法可用于为每个选定元素调用任意代码，对于创建同时访问父数据和子数据的上下文非常有用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<p foo=\"goo\" class=\"goo\" style=\"font-size: 10px;\">Sun Apr 19 2020 13:52:01 GMT+0800 (China Standard Time) <span style=\"color: blue;\">D3.js</span></p><div style=\"background-color: rgb(204, 204, 204); width: 100px; height: 100px; margin: 10px;\"><p style=\"text-align: center; color: white;\">0</p></div><div style=\"background-color: rgb(204, 204, 204); width: 100px; height: 100px; margin: 10px;\"><p style=\"text-align: center; color: white;\">1</p></div><div style=\"background-color: rgb(204, 204, 204); width: 100px; height: 100px; margin: 10px;\"><p style=\"text-align: center; color: white;\">2</p></div>"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// 选中所有的div元素，统一设定样式，分别设定内容\n",
    "d3.selectAll(\"div\")\n",
    "    .style(\"background-color\", \"#ccc\")\n",
    "    .style(\"width\", \"100px\")\n",
    "    .style(\"height\", \"100px\")\n",
    "    .style(\"margin\", \"10px\")\n",
    "    .each(function(d, i) {\n",
    "        d3.select(this)\n",
    "            .append(\"p\")\n",
    "            .text(i)\n",
    "            .style(\"text-align\", \"center\")\n",
    "            .style(\"color\", \"white\");\n",
    "    });\n",
    "$$.html(document.body.innerHTML);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`d3.select(this)`这个选集是一个用变量this表示的且包含当前DOM元素的单元素选集。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用子选择器\n",
    "\n",
    "可以在select方法中还可使用select，从而达到更加灵活的处理。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<p foo=\"goo\" class=\"goo\" style=\"font-size: 10px;\">Sun Apr 19 2020 13:52:01 GMT+0800 (China Standard Time) <span style=\"color: blue;\">D3.js</span></p><div style=\"background-color: rgb(187, 187, 187); width: 100px; height: 100px; margin: 10px;\"><p style=\"text-align: center; color: white;\">0</p></div><div style=\"background-color: rgb(187, 187, 187); width: 100px; height: 100px; margin: 10px;\"><p style=\"text-align: center; color: white;\">1</p></div><div style=\"background-color: rgb(187, 187, 187); width: 100px; height: 100px; margin: 10px;\"><p style=\"text-align: center; color: white;\">2</p></div>"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d3.select(\"body\")\n",
    "    .style(\"margin\", \"10px auto\")\n",
    "    .selectAll(\"div\")\n",
    "    .style(\"background-color\", \"#bbb\")\n",
    "$$.html(document.body.innerHTML);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 获得原始元素\n",
    "\n",
    "在某些时候，我们可能需要获得原始的DOM元素，这个时候，可以使用node()方法，该方法会将选择的元素节点，以数组形式返回。在某些情况下，特别是D3与其他JavaScript库搭配使用时，原始选集访问方式特别方便，因为其他库无法使用D3选集而只能使用原始的DOM元素。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[\n",
       "  HTMLDivElement {\n",
       "    [Symbol(SameObject caches)]: [Object: null prototype] { style: [CSSStyleDeclaration] }\n",
       "  },\n",
       "  HTMLDivElement {\n",
       "    [Symbol(SameObject caches)]: [Object: null prototype] { style: [CSSStyleDeclaration] }\n",
       "  },\n",
       "  HTMLDivElement {\n",
       "    [Symbol(SameObject caches)]: [Object: null prototype] { style: [CSSStyleDeclaration] }\n",
       "  }\n",
       "]"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d3.selectAll(\"div\").nodes()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 参考资料\n",
    "\n",
    "1. <https://github.com/d3/d3/blob/master/API.md#selections-d3-selection>\n",
    "1. <https://github.com/d3/d3-selection/blob/master/README.md>"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "幻灯片",
  "kernelspec": {
   "display_name": "Javascript (Node.js)",
   "language": "javascript",
   "name": "javascript"
  },
  "language_info": {
   "file_extension": ".js",
   "mimetype": "application/javascript",
   "name": "javascript",
   "version": "13.13.0"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {
    "height": "134px",
    "width": "238px"
   },
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
